优化与求根(scipy.optimize)¶

常用功能

  • 最小化(或最大化)目标函数,包括约束优化和无约束优化,包括非线性问题的求解器(支持局部和全局优化算法)
  • 线性规划算法
  • 约束和非线性最小二乘法
  • 求根
  • 曲线拟合

代码¶

  • 标量函数(Scalar function)优化 ```python def f(x): return (x - 2) x (x + 2)**2

from scipy.optimize import minimize_scalar res = minimize_scalar(f) res.x

res = minimize_scalar(f, bounds=(-3, -1), method='bounded') res.x


- 局部多变量优化

```python
from scipy.optimize import minimize
def f2(x):
    return (x[0] - 2) * x[1] * (x[2] + 2)**2

x0 = [1.3, 0.7, 0.8]
res = minimize(f2, x0, method='Nelder-Mead', tol=1e-6)
res.x

1.2807764040333458

from scipy.optimize import minimize
def f2(x):
    return (x[0] - 2) * x[1] * (x[2] + 2)**2


x0 = [1.3, 0.7, 0.8]
res = minimize(f2, x0, method='Nelder-Mead', tol=1e-6)
res.x

array([-1.90682839e+53, 5.63973935e+52, 3.40181690e+52])

fun = lambda x: (x[0] - 1)**2 + (x[1] - 2.5)**2
cons = ({'type': 'ineq', 'fun': lambda x:  x[0] - 2 * x[1] + 2},
        {'type': 'ineq', 'fun': lambda x: -x[0] - 2 * x[1] + 6},
        {'type': 'ineq', 'fun': lambda x: -x[0] + 2 * x[1] + 2})
# Constraint type: eq = equality, ineq = inequality.
bnds = ((0, None), (0, None))
res = minimize(fun, (2, 0), method='SLSQP', bounds=bnds,constraints=cons)
res

fun: 0.8000000011920985 jac: array([ 0.80000002, -1.59999999]) message: 'Optimization terminated successfully' nfev: 10 nit: 3 njev: 3 status: 0 success: True x: array([1.4, 1.7])

全局(Global)优化¶

  • basinhopping: basin-hopping算法
  • brute:在给定范围内用蛮力最小化函数
  • differential_evolution:使用差分进化算法找到多元函数的全局最小值
  • shgo:使用SHG优化方法找到函数的全局最小值。
  • dual_annealing:使用对偶退火找到一个函数的全局最小值
from scipy.optimize import dual_annealing
func = lambda x: np.sum(x*x - 10*np.cos(2*np.pi*x)) + 10*np.size(x)
lw = [-5.12] * 10
up = [5.12] * 10
ret = dual_annealing(func, bounds=list(zip(lw, up)), seed=1234)
ret.x

array([-5.77693816e-09, -6.21011760e-09, -7.32589580e-09, -7.58941522e-09, -4.62162558e-09, -5.02143869e-09, -5.95222774e-09, -5.74646040e-09, -5.25916080e-09, -4.84366783e-09])

from scipy.optimize import differential_evolution
import numpy as np
def ackley(x):
    arg1 = -0.2 * np.sqrt(0.5 * (x[0] ** 2 + x[1] ** 2))
    arg2 = 0.5 * (np.cos(2. * np.pi * x[0]) + np.cos(2. * np.pi * x[1]))
    return -20. * np.exp(arg1) - np.exp(arg2) + 20. + np.e
bounds = [(-5, 5), (-5, 5)]
result = differential_evolution(ackley, bounds)
result.x, result.fun

(array([0., 0.]), 4.440892098500626e-16)

求根(Root)¶

from scipy import optimize

def fun(x):
    return [x[0]  + 0.5 * (x[0] - x[1])**3 - 1.0,
            0.5 * (x[1] - x[0])**3 + x[1]]

def jac(x):
    return np.array([[1 + 1.5 * (x[0] - x[1])**2,
                      -1.5 * (x[0] - x[1])**2],
                     [-1.5 * (x[1] - x[0])**2,
                      1 + 1.5 * (x[1] - x[0])**2]])

sol = optimize.root(fun, [0, 0], jac=jac, method='hybr')
sol.x

线性规划(linear programming)¶

c = [-1, 4]
A = [[-3, 1], [1, 2]]
b = [6, 4]
x0_bounds = (None, None)
x1_bounds = (-3, None)
from scipy.optimize import linprog
res = linprog(c, A_ub=A, b_ub=b, bounds=[x0_bounds, x1_bounds])
res
 con: array([], dtype=float64)
 fun: -21.99999984082497

message: 'Optimization terminated successfully.' nit: 6 slack: array([3.89999997e+01, 8.46872172e-08]) status: 0 success: True x: array([ 9.99999989, -2.99999999])

指派问题

from scipy.optimize import linear_sum_assignment

goodAt =np.array([[7, 3, 7, 4, 5, 5],[7, 3, 7, 4, 5, 5],
       [4, 9, 2, 6, 8, 3],[4, 9, 2, 6, 8, 3],
       [8, 3, 5, 7, 6, 4],[8, 3, 5, 7, 6, 4],
       [4, 6, 2, 3, 7, 8],[4, 6, 2, 3, 7, 8]])
weakAt=10-goodAt
row_ind,col_ind=linear_sum_assignment(weakAt)
print(row_ind)#开销矩阵对应的行索引
print(col_ind)#对应行索引的最优指派的列索引

In [1]:
def f(x):
    return (x - 2) * x * (x + 2)**2

from scipy.optimize import minimize_scalar
res = minimize_scalar(f)
res.x
Out[1]:
1.2807764040333458
In [2]:
res = minimize_scalar(f, bounds=(-3, -1), method='bounded')
res.x
Out[2]:
-2.000000202597239
In [3]:
from scipy.optimize import minimize
def f2(x):
    return (x[0] - 2) * x[1] * (x[2] + 2)**2


x0 = [1.3, 0.7, 0.8]
res = minimize(f2, x0, method='Nelder-Mead', tol=1e-6)
res.x
Out[3]:
array([-1.90682839e+53,  5.63973935e+52,  3.40181690e+52])
In [4]:
res = minimize(f2, x0, method='BFGS', tol=1e-6)
res.x
Out[4]:
array([-688.19333084,  690.19333458,  345.54666916])
In [5]:
fun = lambda x: (x[0] - 1)**2 + (x[1] - 2.5)**2
cons = ({'type': 'ineq', 'fun': lambda x:  x[0] - 2 * x[1] + 2},
        {'type': 'ineq', 'fun': lambda x: -x[0] - 2 * x[1] + 6},
        {'type': 'ineq', 'fun': lambda x: -x[0] + 2 * x[1] + 2})
# Constraint type: eq = equality, ineq = inequality.
bnds = ((0, None), (0, None))
res = minimize(fun, (2, 0), method='SLSQP', bounds=bnds,constraints=cons)
res
Out[5]:
     fun: 0.8000000011920985
     jac: array([ 0.80000002, -1.59999999])
 message: 'Optimization terminated successfully'
    nfev: 10
     nit: 3
    njev: 3
  status: 0
 success: True
       x: array([1.4, 1.7])
In [6]:
from scipy.optimize import dual_annealing
func = lambda x: np.sum(x*x - 10*np.cos(2*np.pi*x)) + 10*np.size(x)
lw = [-5.12] * 10
up = [5.12] * 10
ret = dual_annealing(func, bounds=list(zip(lw, up)), seed=1234)
ret.x
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Input In [6], in <cell line: 5>()
      3 lw = [-5.12] * 10
      4 up = [5.12] * 10
----> 5 ret = dual_annealing(func, bounds=list(zip(lw, up)), seed=1234)
      6 ret.x

File D:\softwares\anaconda3\lib\site-packages\scipy\optimize\_dual_annealing.py:641, in dual_annealing(func, bounds, args, maxiter, local_search_options, initial_temp, restart_temp_ratio, visit, accept, maxfun, seed, no_local_search, callback, x0)
    639 # Initialization of the energy state
    640 energy_state = EnergyState(lower, upper, callback)
--> 641 energy_state.reset(func_wrapper, rand_state, x0)
    642 # Minimum value of annealing temperature reached to perform
    643 # re-annealing
    644 temperature_restart = initial_temp * restart_temp_ratio

File D:\softwares\anaconda3\lib\site-packages\scipy\optimize\_dual_annealing.py:172, in EnergyState.reset(self, func_wrapper, rand_gen, x0)
    170 reinit_counter = 0
    171 while init_error:
--> 172     self.current_energy = func_wrapper.fun(self.current_location)
    173     if self.current_energy is None:
    174         raise ValueError('Objective function is returning None')

File D:\softwares\anaconda3\lib\site-packages\scipy\optimize\_dual_annealing.py:380, in ObjectiveFunWrapper.fun(self, x)
    378 def fun(self, x):
    379     self.nfev += 1
--> 380     return self.func(x, *self.args)

Input In [6], in <lambda>(x)
      1 from scipy.optimize import dual_annealing
----> 2 func = lambda x: np.sum(x*x - 10*np.cos(2*np.pi*x)) + 10*np.size(x)
      3 lw = [-5.12] * 10
      4 up = [5.12] * 10

NameError: name 'np' is not defined
In [ ]:
func2 = lambda x:(x[0] - 2) * x[1] * (x[2] + 2)**2
lw = [-5.12] * 3
up = [5.12] * 3
ret = dual_annealing(func, bounds=list(zip(lw, up)), seed=1234)
ret.x
In [ ]:
from scipy.optimize import differential_evolution
import numpy as np
def ackley(x):
    arg1 = -0.2 * np.sqrt(0.5 * (x[0] ** 2 + x[1] ** 2))
    arg2 = 0.5 * (np.cos(2. * np.pi * x[0]) + np.cos(2. * np.pi * x[1]))
    return -20. * np.exp(arg1) - np.exp(arg2) + 20. + np.e
bounds = [(-5, 5), (-5, 5)]
result = differential_evolution(ackley, bounds)
result.x, result.fun
In [ ]:
from scipy import optimize

def fun(x):
    return [x[0]  + 0.5 * (x[0] - x[1])**3 - 1.0,
            0.5 * (x[1] - x[0])**3 + x[1]]

def jac(x):
    return np.array([[1 + 1.5 * (x[0] - x[1])**2,
                      -1.5 * (x[0] - x[1])**2],
                     [-1.5 * (x[1] - x[0])**2,
                      1 + 1.5 * (x[1] - x[0])**2]])

sol = optimize.root(fun, [0, 0], jac=jac, method='hybr')
sol.x
In [ ]:
#help(optimize.root)
In [ ]:
c = [-1, 4]
A = [[-3, 1], [1, 2]]
b = [6, 4]
x0_bounds = (None, None)
x1_bounds = (-3, None)
from scipy.optimize import linprog
res = linprog(c, A_ub=A, b_ub=b, bounds=[x0_bounds, x1_bounds])
res
In [ ]:
from scipy.optimize import linear_sum_assignment

goodAt =np.array([[7, 3, 7, 4, 5, 5],[7, 3, 7, 4, 5, 5],
       [4, 9, 2, 6, 8, 3],[4, 9, 2, 6, 8, 3],
       [8, 3, 5, 7, 6, 4],[8, 3, 5, 7, 6, 4],
       [4, 6, 2, 3, 7, 8],[4, 6, 2, 3, 7, 8]])
weakAt=10-goodAt
row_ind,col_ind=linear_sum_assignment(weakAt)
print(row_ind)#开销矩阵对应的行索引
print(col_ind)#对应行索引的最优指派的列索引